﻿/************************************************************************
 * SMLib - State Model Library
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     
 * Description  :     
 * Change log   :     
 * To Do        :
 ************************************************************************/
using System;
using System.Collections.Generic;
using SMLib.Base;
using SMLib.Automata.Base;
using SMLib.Interoperability.DOT;

namespace SMLib.Automata.Moore
{
    /// <summary>
    /// Moore Machine
    /// </summary>
    public class MooreMachine : DiGraph<MooreMachine.Moore_State, MooreMachine.Moore_Transition>
    {
        #region Moore Node
        /// <summary>
        /// Defenition of Moore Node
        /// </summary>
        public class Moore_State : IState<Moore_Transition>
        {
            /// <summary>
            /// Use refrence for equality check. If it is true, the "Equals" method 
            /// works based on equality of references (instead of equality of content of objects)
            /// </summary>
            public static bool UseRefForEquality = true;

            /// Identifiers of nodes are "string"
            string id = "";
            List<Moore_Transition> tr;


            ISymbol output;

            /// <summary>
            /// The output of this state
            /// </summary>
            public ISymbol Output
            {
                get
                {
                    return output;
                }
            }

            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="ID"></param>
            public Moore_State(string ID, ISymbol output)
            {
                id = ID;
                this.output = output;
                tr = new List<Moore_Transition>();
            }


            /// <summary>
            /// We consider two states equal iff they have same ID
            /// <seealso cref="UseRefForEquality"/>
            /// </summary>
            /// <param name="obj">the other object to be compared with</param>
            /// <returns>true if they are equal, false in other case</returns>
            public override bool Equals(object obj)
            {
                if (UseRefForEquality)
                {
                    return object.ReferenceEquals(this, obj);
                }
                if (obj is Moore_State)
                {
                    return (((Moore_State)obj).id == id && ((Moore_State)obj).output.Equals(output));
                }
                return false;
            }


            public bool HasTransitionTo(Moore_State n)
            {
                foreach (Moore_Transition t in Transitions)
                    if (t.Destination == n)
                        return true;
                return false;
            }

            /// <summary>
            /// It should be deterministic!
            /// For nondeterministic Moore machine, use "ListOfAllNextNodes" instead.
            /// </summary>
            /// <returns></returns>
            public Moore_State NextNode(ISymbol input)
            {
                foreach (Moore_Transition t in Transitions)
                {
                    if (t.Equals(input))
                    {
                        return (Moore_State)t.Destination;
                    }
                }
                return null;
            }

            /// <summary>
            /// Add a transition to the state (it will always add the transition and doesn't care about determinisim)
            /// </summary>
            /// <param name="dest">destination of transition</param>
            /// <param name="symb">symbol on the transition</param>
            /// <returns>the added transition</returns>
            public Moore_Transition AddTransition(Moore_State dest, ISymbol symb)
            {
                Moore_Transition t = new Moore_Transition(symb);
                t.SetDestination(dest);
                tr.Add(t);
                return t;
            }


           

            #region IStateContainer Members

            public object Identifier
            {
                get { return id; }
            }

            public List<Moore_Transition> Transitions
            {
                get { return tr; }
            }

            public object content
            {
                get { return id; }
                set { this.id = value.ToString(); }
            }

            #endregion

            public override string ToString()
            {
                return id + "/" + output;
            }

            public override int GetHashCode()
            {
                return base.GetHashCode();
            }
        }
        #endregion

        #region Moore Transition
        /// <summary>
        /// Defenition of Moore Transition
        /// </summary>
        public class Moore_Transition //: ITransition<Moore_State>
        {

            Moore_State dst;
            ISymbol input;

            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="inp">the input symbol</param>
            /// <param name="outp">the output symbol</param>
            public Moore_Transition(ISymbol inp)
            {
                dst = null;
                input = inp;
            }



            /// <summary>
            /// String representation of a Moore transition
            /// </summary>
            /// <returns>the string value of this transition</returns>
            public override string ToString()
            {
                try
                {
                    return input.ToString();
                }
                catch
                {
                    return "";
                }
            }

            /// <summary>
            /// Set destination of a transition
            /// </summary>
            /// <param name="node">the target state</param>
            public void SetDestination(Moore_State node)
            {
                dst = node;
            }

            /// <summary>
            /// The input symbol of transition
            /// </summary>
            public ISymbol Label
            {
                get { return input; }
            }


            #region ITransition Members

            public Moore_State Destination
            {
                get { return dst; }
            }

            #endregion
        }

        #endregion


        /// <summary>
        /// Constrctor
        /// </summary>
        /// <param name="inputAlphabet">The input alphabet</param>
        /// <param name="outputAlphabet">The output alphabet</param>
        public MooreMachine(Alphabet inputAlphabet = null, Alphabet outputAlphabet = null)
        {
            this.InputAlphabet = inputAlphabet;
            this.OutputAlphabet = outputAlphabet;
        }



        public Alphabet InputAlphabet;

        ///// <summary>
        ///// The input alphabet 
        ///// </summary>
        //public Alphabet InputAlphabet
        //{
        //    get
        //    {
        //        return inputAlphabet;
        //    }
        //}

        /// <summary>
        /// The output alphabet 
        /// </summary>
        public Alphabet OutputAlphabet;

        /// <summary>
        /// Read Moore machine from a DOT file. 
        /// </summary>
        /// <param name="inputFile">the full path of input text file</param>
        /// <returns>the Moore machine</returns>
        public static MooreMachine ReadFromDOT(string inputFile)
        {
            MooreMachine m = new MooreMachine();
            /*m.InputAlphabet = new Alphabet();
            m.OutputAlphabet = new Alphabet();

            DotFormat dotReader = new DotFormat();
            string ilbl, olbl;
            foreach (DotFormat.DotTransition tr in dotReader.ReadDotFile(inputFile))
            {
                ilbl = tr.Label.Substring(0, tr.Label.IndexOf("/"));

                if (!m.InputAlphabet.Contains(new StringSymbol(ilbl)))
                    m.InputAlphabet.AddSymbol(new StringSymbol(ilbl));


                olbl = tr.Label.Substring(tr.Label.IndexOf("/") + 1, tr.Label.Length - tr.Label.IndexOf("/") - 1);

                if (!m.OutputAlphabet.Contains(new StringSymbol(olbl)))
                    m.OutputAlphabet.AddSymbol(new StringSymbol(olbl));

                Moore_State srcNode = null, dstNode = null;
                foreach (Moore_State n in m.states)
                {
                    if (((string)n.Identifier).Equals(tr.Source))
                        srcNode = n;
                    if (((string)n.Identifier).Equals(tr.Destination))
                        dstNode = n;

                    if (dstNode != null && srcNode != null)
                        break;
                }
                if (srcNode == null)
                {
                    srcNode = new Moore_State(tr.Source);
                    m.states.Add(srcNode);

                    if (tr.Destination == tr.Source)
                        dstNode = srcNode;
                }

                if (dstNode == null)
                {
                    dstNode = new Moore_State(tr.Destination);
                    m.states.Add(dstNode);
                }

                Moore_Transition t = new Moore_Transition(new StringSymbol(ilbl), new StringSymbol(olbl));
                t.SetDestination(dstNode);
                srcNode.Transitions.Add(t);
            }*/
            return m;
        }

        /// <summary>
        /// Read DFA from a text file. 
        /// <remarks>Each line of file should have the following pattern:
        /// "src" -&gt; "dst" : "label"</remarks>
        /// </summary>
        /// <param name="inputFile">the complete address of input file</param>
        /// <returns>the Moore machine</returns>
        public static MooreMachine readMachine(string inputFile)
        {
            MooreMachine m = new MooreMachine();
            /*string line;
            m.InputAlphabet = new Alphabet();
            m.OutputAlphabet = new Alphabet();
            if (!System.IO.File.Exists(inputFile))
                throw new Exception("Fatal error: the file '" + inputFile + "' does not exist!");

            // Read the file AND display it line by line.
            System.IO.StreamReader file = new System.IO.StreamReader(inputFile);
            while ((line = file.ReadLine()) != null)
            {
                int i, j;
                i = line.IndexOf("\"");
                j = line.IndexOf("\"", i + 1);
                string src, dst, ilbl, olbl;
                src = line.Substring(i + 1, j - i - 1);

                i = line.IndexOf("->");
                i = line.IndexOf("\"", i + 1);
                j = line.IndexOf("\"", i + 1);
                dst = line.Substring(i + 1, j - i - 1);

                i = line.IndexOf(":");
                i = line.IndexOf("\"", i + 1);
                j = line.IndexOf("\"", i + 1);
                ilbl = line.Substring(i + 1, j - i - 1);

                if (!m.InputAlphabet.Contains(new StringSymbol(ilbl)))
                    m.InputAlphabet.AddSymbol(new StringSymbol(ilbl));


                i = line.IndexOf("-", j);
                i = line.IndexOf("\"", i + 1);
                j = line.IndexOf("\"", i + 1);
                olbl = line.Substring(i + 1, j - i - 1);

                if (!m.OutputAlphabet.Contains(new StringSymbol(olbl)))
                    m.OutputAlphabet.AddSymbol(new StringSymbol(olbl));

                Moore_State srcNode = null, dstNode = null;
                foreach (Moore_State n in m.states)
                {
                    if (((string)n.Identifier).Equals(src))
                        srcNode = n;
                    if (((string)n.Identifier).Equals(dst))
                        dstNode = n;

                    if (dstNode != null && srcNode != null)
                        break;
                }
                if (srcNode == null)
                {
                    srcNode = new Moore_State(src);
                    m.states.Add(srcNode);

                    if (dst == src)
                        dstNode = srcNode;
                }

                if (dstNode == null)
                {
                    dstNode = new Moore_State(dst);
                    m.states.Add(dstNode);
                }

                Moore_Transition t = new Moore_Transition(new StringSymbol(ilbl), new StringSymbol(olbl));
                t.SetDestination(dstNode);
                srcNode.Transitions.Add(t);
            }*/
            return m;
        }
    	
    	
		public override MooreMachine.Moore_State DestinationOf(MooreMachine.Moore_Transition t)
		{
			return t.Destination;
		}
    }
}
